from fastapi import FastAPI
from starlette.requests import Request
import os
from fastapi.templating import Jinja2Templates
from starlette.staticfiles import StaticFiles
from starlette.responses import RedirectResponse
from typing import List
from fastapi import FastAPI, File, UploadFile
from starlette.responses import HTMLResponse
import json
import uvicorn
import gdchinait
from tinydb import TinyDB, Query
import heapq
#database
import os
import chardet
from tinydb import TinyDB, Query
import docx2txt
#from xlsx2csv import Xlsx2csv
#import pdftotext
from pdfminer.pdfparser import PDFParser, PDFDocument
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox, LTTextLine

def tab_html(title,headers0,rows):
    start='''
    <table class="table table-condensed">
        <thead>
            <tr>
                <th>#</th>
                <th>Firstname</th>
                <th>2nname</th>
            </tr>
        </thead>
        '''
    headers='<body><h4 style="text-align:center">'+title+'</h4><table class="table table-bordered table-striped"><thead><tr>'
    for item in headers0:
        headers=headers+'<th>'+item+'</th>'
    headers=headers+'</tr></thead>'
    body='<tbody>'
    for item in rows:
        body=body+'<tr>'
        for x in item:
            body=body+'<td>'+str(x)+'</td>'
        body=body+'</tr>'
    body=body+'</tbody></table></body>'
    return headers+body

#word,excel,ppt,txt,pdf不同类型的文档选择不同的处理方式
def file2txt(file):
    if os.path.basename(file).endswith('.txt') or os.path.basename(file).endswith('.TXT'):
        return txt2txt(file)
    if os.path.basename(file).endswith('.docx') or os.path.basename(file).endswith('.DOCX'):
        return docxtotxt(file)
    if os.path.basename(file).endswith('.xlsx') or os.path.basename(file).endswith('.XLSX'):
        return xlsx2txt(file)
    if os.path.basename(file).endswith('.pptx') or os.path.basename(file).endswith('.PPTX'):
        return pptx2txt(file)
    if os.path.basename(file).endswith('.pdf') or os.path.basename(file).endswith('.PDF'):
        return pdf2txt(file)


#TXT文档的处理方式，判断编码类型，统一改编码为UTF-8码
def txt2txt(file):
    with open(file,mode='rb') as f3:
        data = f3.read()
    result = chardet.detect(data)


    with open(file,'r',encoding=result['encoding']) as f:
        content=' '.join(f.readlines())
    if "UTF" in str(result['encoding']):
        pass
    else:
        with open(file,'w',encoding='UTF-8') as f:
            f.write(content)

    return {'abspath':os.path.abspath(file),'dirname':os.path.dirname(file),'basename':os.path.basename(file),'path0':os.path.dirname(file)+'/'+os.path.basename(file),'mtime':os.path.getmtime(file),'content':content}

#Word文档的处理方式
def docxtotxt(file):
    content = docx2txt.process(file)

    return {'abspath':os.path.abspath(file),'dirname':os.path.dirname(file),'basename':os.path.basename(file),'path0':os.path.dirname(file)+'/'+os.path.basename(file),'mtime':os.path.getmtime(file),'content':content}

#Xlsx文档的处理方式
def xlsx2txt(file):
    Xlsx2csv(file, outputencoding="utf-8").convert("tmp.txt")
    with open('tmp.txt','r',encoding='utf-8') as f:
        content=' '.join(f.readlines())

    return {'abspath':os.path.abspath(file),'dirname':os.path.dirname(file),'basename':os.path.basename(file),'path0':os.path.dirname(file)+'/'+os.path.basename(file),'mtime':os.path.getmtime(file),'content':content}

#PPTX文档的处理方式
def pptx2txt(file):
    print('python3 -m pptx2md {}'.format(file))
    os.system('python3 -m pptx2md {}'.format(file))
    with open('out.md','r',encoding='utf-8') as f:
        content=' '.join(f.readlines())

    return {'abspath':os.path.abspath(file),'dirname':os.path.dirname(file),'basename':os.path.basename(file),'path0':os.path.dirname(file)+'/'+os.path.basename(file),'mtime':os.path.getmtime(file),'content':content}

#pdf文档的处理方式
def pdf2txt(file):
    fp = open(file, 'rb')


    parser = PDFParser(fp)
    doc = PDFDocument()
    parser.set_document(doc)
    doc.set_parser(parser)
    doc.initialize('')
    rsrcmgr = PDFResourceManager()
    laparams = LAParams()
    laparams.char_margin = 1.0
    laparams.word_margin = 1.0
    device = PDFPageAggregator(rsrcmgr, laparams=laparams)
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    extracted_text = ''

    for page in doc.get_pages():
        interpreter.process_page(page)
        layout = device.get_result()
        for lt_obj in layout:
            if isinstance(lt_obj, LTTextBox) or isinstance(lt_obj, LTTextLine):
                #print(lt_obj.get_text())
                extracted_text += lt_obj.get_text()

    content = extracted_text
    fp.close()

    return {'abspath':os.path.abspath(file),'dirname':os.path.dirname(file),'basename':os.path.basename(file),'path0':os.path.dirname(file)+'/'+os.path.basename(file),'mtime':os.path.getmtime(file),'content':content}



#新增和更新数据库
def db_insert(file):
    #file='gdchinait/ip.txt'
    User = Query()
    #print(os.path.getmtime(file))
    db = TinyDB('knowledge.json')
    if db.search(User.mtime == os.path.getmtime(file)):
        print('pass',file)
        pass
    elif db.search(User.abspath==os.path.abspath(file)):#db.search(User.abspath=os.path.abspath(file)):
        db.update({'content':file2txt(file)['content'],'mtime':file2txt(file)['mtime']},User.abspath==os.path.abspath(file))
        #db.close()
        print('update',file)
    else:
        print('insert',file)
        db.insert(file2txt(file))
    #print(db.all())
    db.close()

#历遍目录
location='static'
def tree0(locationx):
    z=[]
    for root, dirs, files in os.walk(locationx, topdown=False):
        for name in files:
            path0=os.path.join(root, name).replace('\\','/')
            if name.startswith('~'):
                pass
            elif name.endswith('.txt') or name.endswith('.TXT') or name.endswith('.docx') or name.endswith('.DOCX') or name.endswith('.xlsx') or name.endswith('.XLSX') or name.endswith('.pptx') or name.endswith('.PPTX') or name.endswith('.pdf'):
                if ' ' in name or ' ' in name:
                    print('*************',name,'**************')
                #print(path0)
                #txt2txt(path0)
                db_insert(path0)

                z.append(path0)
        for name in dirs:
            if ' ' in name or ' ' in name:
                print('*************',name,'**************')
            z.append(os.path.join(root, name).replace('\\','/'))
    z.sort()
    #print(z)
    return z



#删除旧的没有对应路径的数据库记录
def updatedb():
    list0=tree0(location)
    User = Query()
    db = TinyDB('knowledge.json')
    list1=list(map(lambda x:x['path0'],db.all()))

    for item in list1:
        if item in list0:
            pass
            #print(item,'yes')
        else:
            print(item,'not existed, will be remove')
            db.remove(User.abspath==os.path.abspath(item))
    db.close()

for root, dirs, files in os.walk(".", topdown=False):
    for name in files:
        for item in [' ',' ','(',')',',','（','）','，','。']:
            if item in name:
                try:
                    os.rename(os.path.join(root, name),os.path.join(root, name).replace(item,'_'))
                except:
                    pass
        #print(os.path.join(root, name))
    for name in dirs:
        for item in [' ',' ','(',')',',','（','）','，','。']:
            if item in name:
                try:
                    os.rename(os.path.join(root, name),os.path.join(root, name).replace(item,'_'))
                except:
                    pass
        #print(os.path.join(root, name))


location='static'
location1='static1'
app = FastAPI()
app.mount('/static', StaticFiles(directory=location), name='static')
app.mount('/static1', StaticFiles(directory=location1), name='static1')
#app.mount('/static0', StaticFiles(directory='static0'), name='static0')



@app.get("/static")
def static0(item_id: str=None,q: str = ''):
    print('q:',q)
    #listfiles=os.listdir(location)
    def web(locationx):
        print('locationx:',locationx)
        if os.path.isfile(locationx):
            #return RedirectResponse(url=locationx)
            return '<dir><a href={}>{}</a></dir>'.format(locationx,locationx).replace('//','/')
        z=''

        for item in os.listdir(locationx):
            print(item,locationx+item)
            if os.path.isfile(locationx+'/'+item):
                print('isfile',locationx+'/'+item)
                z=z=z+'<dir><a href={}>{}</a></dir>'.format('/'+locationx+'/'+item,item)
            elif os.path.isdir(locationx+'/'+item):
                z=z=z+'<dir><a href={}>{}</a></dir>'.format('?q='+q+'/'+item,item)
        z=z.replace('//','/')
        return z


    title='''
    <div class="jumbotron">
      <h3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{}</h3>
      '''

    return HTMLResponse(content=gdchinait.html.start+title.format(q)+web(location+'/'+q))

@app.get("/left")
def toplevel():
    #print('q:',q)
    #listfiles=os.listdir(location)
    location='static'
    z=''
    for item in os.listdir(location):
        if os.path.isdir(location+'/'+item):
            #z=z+'<dir><a href={} target="right1">{}</a></dir>'.format('static?q='+item,item)
            z=z+'<dir><li role="presentation"><a href={} target="right1">{}</a></li></dir>'.format('static?q='+item,item)







    z='<dir><a href=/jumbotron target="right1">知识库</a></dir>'+'<dir><a href={} target="right1">{}</a></dir>'.format('tree','知识树')\
       +'<dir><a href=/upload target="right1">上传</a></dir>'+z+'<dir><a href=/static/office_google_chrome.rar target="right1">office插件</a></dir> \
'
    return HTMLResponse(content=gdchinait.html.start+'<ul class="nav nav-pills">'+z+'</ul>')

#知识树，所有文档目录
@app.get("/tree")
def tree():
    location='static'
    def tree0(locationx):
        z=[]
        for root, dirs, files in os.walk(locationx, topdown=False):
            for name in files:
                z.append(os.path.join(root, name).replace('\\','/'))
            for name in dirs:
                z.append(os.path.join(root, name).replace('\\','/'))
        z.sort()
        return z
    z=''
    for item in tree0(location):
        item=item.replace('static','static?q=')
        z=z+'<dir><a href={}>{}</a></dir>'.format(item,item[10:])

    title='''
    <div class="jumbotron">
      <h2>&nbsp;&nbsp;&nbsp;&nbsp;    知识树</h2>
      <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  汇集所有文档目录，帮助您直达关键文档</p>
      '''
    return HTMLResponse(content=gdchinait.html.start+title+z)

@app.get("/search0/")
def search0():

    db = TinyDB('knowledge.json')

    return list(map(lambda x:x[1],heapq.nlargest(5, list(map(lambda x:[x['mtime'],x['path0']],db.all())))))

#搜索目录
@app.get("/search")
def search(q: str = None):
    inputa='''
    <h2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;知识库</h2>
    <form class="form-inline" action="/search" method="get">
      <div class="form-group">

        <input  class="form-control" id=q placeholder={} name="q" style="width:500px">


      <button type="submit" class="btn btn-default">搜索</button>
      </div>
    </form>
        '''
    if q:
        print('q is :',q)
        def db_search(keyword):
            User = Query()
            db = TinyDB('knowledge.json')
            test_func = lambda s: keyword in s
            return db.search(User.content.test(test_func))
        z=''
        for item in db_search(q):
            #z=z+'<dir><a href={}>{}</a></dir>'.format(item['dirname']+'/'+item['basename'],item['basename'])+\
            #   '<div class="panel panel-success">  <div class="panel-body">{}  </div></div>'.format(item['content'])

            #z=z+'<p>'+item['dirname']+'/'+item['basename']+'</p>'
            z=z+'<div class="panel panel-info">  <div class="panel-heading">    <h3 class="panel-title">{}</h3> \
            </div>  <div class="panel-body">    {}  </div></div>'.format('<dir><a href={}>{}</a></dir>'.format(item['path0'],item['path0'][7:]),item['content'].replace(q,'<font size="2" color="red">{}</font>'.format(q)))
        return HTMLResponse(content=gdchinait.html.start+inputa.format(q)+z)
        return HTMLResponse(content=gdchinait.html.start+inputa.format(q)+str(db_search(q)))
    else:

        return HTMLResponse(content=gdchinait.html.start+inputa.format('关键词'))



#在线预览office的chrome插件
@app.get("/chromeoffice")
def chromeoffice():
    return HTMLResponse(content=gdchinait.html.start+'<dir>需要安装chrome的office预览插件，步骤详见下面的视频，含语音</dir><dir>\
<a href="\\\\10.102.10.13\\tools\\100.tmp\\chrome\\office_google_chrome">chrome在线浏览office插件安装路径</a></dir>+<dir><a href="http://10.102.12.248/files/1?q=/Docs/chrome%E5%AE%89%E8%A3%85word%E6%8F%92%E4%BB%B6.mp4">安装插件视频，请点击播放含语音</a></dir>')


#更新文档的方式
@app.get("/update")
def treex():
    a='''
    <button type="button" class="btn btn-lg btn-info" data-toggle="popover" title="如何增改删文档?" data-content="\\\\bjg1filesv4p\\Knowledge_Base\\ 共享网盘式操作，简单方便">右键复制，到资源管理器打开</button>
 '''
    return HTMLResponse(content=gdchinait.html.start+'<dir>右键复制下面的链接， 使用资源管理器（点击开始，粘贴）\
打开</dir><dir><a href="\\\\bjg1filesv4p\\Knowledge_Base\\">\\\\bjg1filesv4p\\Knowledge_Base\\</a></dir>'+a+gdchinait.html.end)


#@app.get("/left")
#def left():
    #return RedirectResponse(url='/lists')
#    return HTMLResponse(content=gdchinait.html.left_page.format(gdchinait.html.head))


#默认主目录
@app.get("/aaa")
def main():
    return RedirectResponse(url='/jumbotron')
    return HTMLResponse(content=gdchinait.html.index)





#默认右边显示
@app.get("/right")
def right():
    return RedirectResponse(url='/jumbotron')

    return HTMLResponse(content=gdchinait.html.start)

#巨幕
@app.get("/")
def jumbotron():
    inputa='''
    <form class="form-inline" action="/search" method="get">
      <div class="form-group">

        <input  class="form-control" id=q placeholder={} name="q" style="width:500px">


      <button type="submit" class="btn btn-default">搜索</button>
      </div>
    </form>
        '''
    x='''
    <div class="jumbotron">
    <p style="text-align:right"><a href="/upload" style="text-align:right">上传</a></p>
    <h1 style="text-align:center">知识库</h1>

      <form class="form-inline" action="/search" method="get" style="text-align:center">
      <div class="form-group">

      <input  class="form-control" id=q placeholder="关键词" name="q" style="width:500px">


      <button type="submit" class="btn btn-primary btn-lg">搜索</button>
      </div>
      </form>
    </div>
    
    '''
    db = TinyDB('knowledge.json')

    #new_updatelist=list(map(lambda x:x[1],heapq.nlargest(5, list(map(lambda x:[x['mtime'],x['path0']],db.all())))))
    new_updatelist=list(map(lambda x:x[1],list(map(lambda x:[x['mtime'],x['path0']],db.all()))))
    #z='<div><div style="float:left;"><a href="/search"><img src="/static0/images/a1.jpg"  alt="上海鲜花港 - 郁金香" /></a></div><div>最新更新文档:</div>'
    z1=tab_html('文档清单:','',list(map(lambda x:['<div><p style="text-align:center"><a href=/{}>{}</a></p></div>'.format(x,x[7:])],new_updatelist)))
    z='最近更新的文档:'
    for item in new_updatelist:
        z=z+'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<div><a href=/{}>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{}</a></div>'.format(item,item)
    z=z+'</div>'
    return HTMLResponse(content=gdchinait.html.start+x+z1)

#默认主目录
@app.get("/about")
def about():

    return HTMLResponse(content=gdchinait.html.start+'<h4><p>软件架构采用python3作为语言，fastapi作为web框架，tinydb作为非关系型数据库</p></h4><p>有问题，找剑波</p>')

#强制更新
@app.get("/forceupdate")
def forceupdate():
    os.system('cd /data && python3 database.py')

    return HTMLResponse(content=gdchinait.html.start+'<h4><p>已强制更新</p></h4>')

@app.post("/uploadfiles/")
async def create_upload_files(
    file: UploadFile = File(...)
):
    #return {"filenames": [file.filename for file in files]}
    #contents = await files[0].read().decode('utf8')
    contents = file.file.read()
    name0=file.filename
    name0=name0.replace(' ','_').replace(' ','_').replace(':','_')
    if name0.endswith('.pdf') or name0.endswith('.PDF') or name0.endswith('.docx') or name0.endswith('.DOCX') or name0.endswith('.txt') or name0.endswith('.TXT'):
        pass
    else:
        return HTMLResponse(content="<p><font color='red'>失败,仅支持pdf,docx,txt格式的文件上传</font></p><p><div><a href=/>返回首页</a></div></p>")
    with open('static/'+name0,'wb') as f:
        f.write(contents)
    #return files[0].filename, files[0].content_type
        content = """
<body>
<a href="/" title="转到CSS5主页">上传完成，请返回首页</a>
</body>
 """.format(name0)
    updatedb()
    return HTMLResponse(content=content)


@app.get("/upload")
async def upload():
    content = """
<body>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="file" type="file" multiple>
<input type="submit">
</form>
</body>
 """
    return HTMLResponse(content=content)

if __name__ == "__main__":
    import webbrowser
    #chrome_path="C:\Program Files\Google\Chrome\Application\chrome.exe"
    #webbrowser.register('chrome', None, webbrowser.BackgroundBrowser(chrome_path))
    #webbrowser.get('chrome').open('http://127.0.0.1:8080')
    webbrowser.open("http://127.0.0.1:8080")
    uvicorn.run(app, host="0.0.0.0",port=8080,log_level="info")
